#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>

#define DEFAULT 8081
#define SIZE 128

class UdpServer{
  private:
    int port;//端口号
    int sockfd;//套接口描述字
    //std::string ip;//IP地址
  public:
    //初始化
    UdpServer(int _port = DEFAULT):port(_port),sockfd(-1)
    {}
    //建立链接
    bool InitUdpServer()
    {
      //socket参数：
      //第一个参数：协议域,常见的协议组用AF_INET AF_INET6 AF_LOCAL AF_ROUTE
      //第二个参数：指定socket的类型，常见的两个参数是：SOCK_STREAM(TCP协议)、SOCK_DGRAM(UDP协议)
      //第三个参数：protocol:协议,常见的协议有IPPROTO_TCP、IPPTOTO_UDP、 IPPROTO_SCTP、IPPROTO_TIPC他们分别对应这TCP传输协议,UDP传输协议,STCP传输协议,TIPC传输协议.当protocol为0时,会自动选择type类型对应的默认协议.
      sockfd = socket(AF_INET, SOCK_DGRAM, 0);
      //套接字小于0说明创建失败
      if(sockfd < 0){
        std::cerr << "socket error" << std::endl;
        return false;
      }

      std::cout << "socket create success, sockfd: " << sockfd << std::endl;//3

			//创建sockaddr_in类型的地址结构
      struct sockaddr_in local;
      memset(&local,'\0',sizeof(local));
      local.sin_family = AF_INET;
      local.sin_port = htons(port);//将主机字节序转换为网络字节序
      //local.sin_addr.s_addr = inet_addr(ip.c_str()); //这里要求整数ip
      local.sin_addr.s_addr = INADDR_ANY;

      //绑定端口号，绑定成功返回0，否则就是绑定失败
      //第一个参数：创建好的套接字sockfd
      //第二个参数：一个const struct sockaddr*指针,根据创建地址结构根据地址创建socket时的地址协议不同而不同.上面的local就是创建好的sockaddr_in结构的地址协议，
			//第三个参数：对应地址的长度
      if(bind(sockfd,(struct sockaddr*)&local, sizeof(local)) < 0){
        std::cerr << "bind error" << std::endl;
        return false;
      }
      std::cerr << "bind sucdess" << std::endl;
      return true;
    }

    //开启服务器
    void Start()
    {
      char buffer[SIZE];

      for(; ; ){
        struct sockaddr_in peer;//peer在这里表示对端
        socklen_t len = sizeof(peer);//peer的长度
        //因为使用的是UDP协议，而UDP是不面向链接的，所以需要拿到数据信息和对端信息（socket,IP,port）
				//recvfrom作用：从套接字里去读sizeof(bufdfer)-1个数据放到buffer里，读取方式设置成0，表示阻塞式读取，后面两个参数是对端信息和对端大小
        ssize_t size = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
        if(size > 0){
          buffer[size] = 0;
          int _port = ntohs(peer.sin_port);//将网络序列转成主机序列
          std::string _ip = inet_ntoa(peer.sin_addr);//对端IP，
          std::cout << _ip << ":" << _port << "#" << buffer << std::endl;
         
          std::string cmd = buffer;
          std::string result;
          if(cmd == "ls"){
            int pipes[2];
            pipe(pipes);//创建匿名管道

            pid_t id = fork();
            if(id == 0) {
              //child
              close(pipes[0]);//关闭子进程的读端
              dup2(pipes[1],1);
              execlp("ls","ls","-a","-l","-i",nullptr);//程序替换
              exit(1);
            }
            close(pipes[1]);//关闭父进程的写端
            char c;
            while(1){
              if(read(pipes[0],&c,1) > 0)//从pipes[0]里读文件放到C里面，每次读取一个字节
              {
                result.push_back(c);
              }
              else{
                break;
              }
            }
            wait(nullptr);//等任意一个子进程
          }

          std::string echo_msg = "server get!->";
          if(result.empty()){//当读取到的是ls命令时将命令输出结果添加到msg
            echo_msg += buffer;
          }
          else{//否则直接输出读取的数据
            echo_msg = result;
          }
          //将读取到的数据返回给服务器
          sendto(sockfd,echo_msg.c_str(),echo_msg.size(),0,(struct sockaddr*)&peer,len);
        }
        else{
          std::cerr << "recvfrom error" << std::endl;
        }
      }
    }
    ~UdpServer()
    {
      if(sockfd >= 0)
        close(sockfd);
    }
};
